﻿using SharpDepend.Datatypes;
using SharpDepend.WP8;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236

namespace SharpDepend.Controls
{
    public sealed class GraphicsDisplay : UserControl, IGraphicsDisplay
    {
        private IGraphicsDevice mCurrentDevice;
        private bool mReady2;
        private List<Texture> mReadyTexturesToRegistrate = new List<Texture>();
        private bool mEnable;
        private bool mRenderTextures;
        private IGraphicsDevice mManualGraphicsDevice;

        public static readonly List<GraphicsDisplay> Instances = new List<GraphicsDisplay>();

        public GraphicsDisplay()
        {
            Instances.Add(this);

            VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
            VerticalContentAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
            HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
            HorizontalContentAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;

            Loaded += GraphicsDisplay_Loaded;
        }

        void GraphicsDisplay_Loaded(object sender, RoutedEventArgs e)
        {
            // If mCurrentDevice is not set. Then this control is created in XAML directly.
            if (mCurrentDevice == null)
            {
                (SharpDepend.Manager.GraphicsDisplayManager as IGraphicsDisplayManager).SetupDisplay(this);
            }
        }
        
        public event Action DeviceLoaded;

        public event Action DeviceUnloaded;

        public IGraphicsDevice ManualGraphicsDevice
        {
            get
            {
                return mManualGraphicsDevice;
            }

            set
            {
                if (mManualGraphicsDevice != value)
                {
                    if (value != null)
                    {
                        GraphicsDeviceChanged(value);
                        value.Initialize(this);
                    }

                    mManualGraphicsDevice = value;
                }
            }
        }

        public void GraphicsDeviceChanged(IGraphicsDevice device)
        {
            if (ManualGraphicsDevice != null)
            {
                return;
            }

            lock (Locker)
            {
                mReady2 = false;

                foreach (var texture in mReadyTexturesToRegistrate)
                {
                    device.RegisterTexture(texture);
                }
                mReadyTexturesToRegistrate.Clear();

                if (mCurrentDevice != null)
                {
                    // Unload textures from old device to the new one.
                    for (int i = mCurrentDevice.Textures.Count - 1; i > -1; --i)
                    {
                        Texture texture = mCurrentDevice.Textures[i];
                        device.RegisterTexture(texture);
                        mCurrentDevice.UnregisterTexture(texture);
                    }

                    // Swich properties.
                    device.Enable = mCurrentDevice.Enable;
                    device.RenderTextures = mCurrentDevice.RenderTextures;

                    // Unload event.
                    System.Action call = DeviceUnloaded;
                    if (call != null)
                    {
                        call();
                    }

                    mCurrentDevice.DisposeDevice();

                    Content = null;
                }

                mCurrentDevice = device;
                Content = (UIElement)device;
                Content.InvalidateArrange();

                // Load event.
                System.Action call2 = DeviceLoaded;
                if (call2 != null)
                {
                    call2();
                }

                mReady2 = true;
            }
        }

        public IGraphicsDevice GraphicsDevice
        {
            get { return mCurrentDevice; }
        }

        public event Classes.EventHandlerMouseDownAction PointDown;

        public event Classes.EventHandlerMouseAction PointMove;

        public event Classes.EventHandlerMouseAction PointUp;

        public event Classes.EventHandlerMouseWheelAction PointWheel;

        public event Classes.EventHandlerMouseAction PointHover;

        public bool OnMouseDown(ref Datatypes.TouchLocation location, int id, int touchCount)
        {
            var call = PointDown;
            if (call != null)
            {
                return call(ref location, id, touchCount);
            }
            return false;
        }

        public void OnMouseMove(ref Datatypes.TouchLocation location, int id, int touchCount)
        {
            var call = PointMove;
            if (call != null)
            {
                call(ref location, id, touchCount);
            }
        }

        public void OnMouseUp(ref Datatypes.TouchLocation location, int id, int touchCount)
        {
            var call = PointUp;
            if (call != null)
            {
                call(ref location, id, touchCount);
            }
        }

        public void OnMouseWheel(ref Datatypes.TouchLocation location)
        {
            var call = PointWheel;
            if (call != null)
            {
                call(ref location);
            }
        }

        public void OnMouseHover(ref Datatypes.TouchLocation location, int id, int touchCount)
        {
            var call = PointHover;
            if (call != null)
            {
                call(ref location, id, touchCount);
            }
        }

        public object Locker
        {
            get { return SharpDepend.Manager.GraphicsDisplayManager; }
        }

        public bool Ready
        {
            get
            {
                var gd = GraphicsDevice;
                return gd != null && gd.Ready && mReady2;
            }
        }

        public bool Enable
        {
            get
            {
                return mEnable;
            }
            set
            {
                mEnable = value;

                var g = GraphicsDevice;
                if (g != null)
                {
                    g.Enable = value;
                }
            }
        }

        public bool RenderTextures
        {
            get
            {
                return mRenderTextures;
            }
            set
            {
                mRenderTextures = value;

                var g = GraphicsDevice;
                if (g != null)
                {
                    g.RenderTextures = value;
                }
            }
        }

        public void RegisterTexture(Datatypes.Texture texture)
        {
            lock (Locker)
            {
                var device = GraphicsDevice;
                if (device != null)
                {
                    GraphicsDevice.RegisterTexture(texture);
                }
                else
                {
                    if (mReadyTexturesToRegistrate.Contains(texture))
                    {
                        throw new Exception("Texture already added.");
                    }
                    mReadyTexturesToRegistrate.Add(texture);
                }
            }
        }

        public void UnregisterTexture(Datatypes.Texture texture)
        {
            lock (Locker)
            {
                var device = GraphicsDevice;
                if (device != null)
                {
                    GraphicsDevice.UnregisterTexture(texture);
                }
                else
                {
                    if (!mReadyTexturesToRegistrate.Contains(texture))
                    {
                        throw new Exception("Texture can not be removed. It do not exist.");
                    }
                    mReadyTexturesToRegistrate.Remove(texture);
                }
            }
        }

        public bool ContainsTexture(Datatypes.Texture texture)
        {
            return GraphicsDevice.Textures.Contains(texture);
        }
    }
}
